/*
 *
 *  *    Copyright 2020-2021 Luter.me
 *  *
 *  *    Licensed under the Apache License, Version 2.0 (the "License");
 *  *    you may not use this file except in compliance with the License.
 *  *    You may obtain a copy of the License at
 *  *
 *  *      http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  *    Unless required by applicable law or agreed to in writing, software
 *  *    distributed under the License is distributed on an "AS IS" BASIS,
 *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  *    See the License for the specific language governing permissions and
 *  *    limitations under the License.
 *
 */

package com.luter.heimdall.servlet.context;

import com.luter.heimdall.core.context.WebRequestHolder;
import com.luter.heimdall.core.exception.HeimdallCookieException;
import com.luter.heimdall.core.utils.StrUtils;
import org.slf4j.Logger;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import static org.slf4j.LoggerFactory.getLogger;

/**
 * Servlet web request 功能实现
 *
 * @author luter
 */
public class ServletWebRequestHolder implements WebRequestHolder {

    private static final transient Logger log = getLogger(ServletWebRequestHolder.class);
    /**
     * The Request. servlet 请求对象，不能为空
     */
    private HttpServletRequest request;

    /**
     * Instantiates a new Servlet web request holder.
     *
     * @param request the request
     */
    public ServletWebRequestHolder(HttpServletRequest request) {
        if (null == request) {
            throw new IllegalArgumentException("HttpServletRequest can not be null ");
        }
        this.request = request;
    }

    @Override
    public String getCookieValue(String name) {
        log.debug("[getCookieValue]:: start resolve cookie from request... Cookie name = [{}]", name);
        if (StrUtils.isBlank(name)) {
            throw new HeimdallCookieException("Wrong Config of Cookie Name ");
        }
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie != null && name.equals(cookie.getName())) {
                    final String value = cookie.getValue();
                    log.debug("[getCookieValue]:: Cookie matched. name = [{}],value= [{}]", name, value);
                    return value;
                }
            }
        }
        return null;
    }

    @Override
    public String getHeaderValue(String name) {
        log.debug("[getHeaderValue]::name = [{}]", name);
        return request.getHeader(name);
    }

    @Override
    public String getQueryValue(String name) {
        log.debug("[getQueryValue]::name = [{}]", name);
        return request.getParameter(name);
    }

    @Override
    public String getRequestURI() {
        log.debug("[getRequestURI]::");
        return request.getRequestURI();
    }

    @Override
    public String getQueryString() {
        log.debug("[getQueryString]::");
        return request.getQueryString();
    }

    @Override
    public String getMethod() {
        log.debug("[getMethod]::");
        return request.getMethod();
    }

    @Override
    public String getRemoteIp() {
        final String realIp = getClientIpAddr(request);
        log.debug("[getRemoteIp]:: ip address = [{}]", realIp);
        return "0:0:0:0:0:0:0:1".equals(realIp) ? "127.0.0.1" : realIp;
    }

    /**
     * Gets request.
     *
     * @return the request
     */
    public HttpServletRequest getRequest() {
        return request;
    }

    private static final String UNKNOWN = "unknown";

    /**
     * Gets real ip.
     *
     * @param request the request
     * @return the real ip
     */
    public static String getRemoteIp(HttpServletRequest request) {
        if (request == null) {
            return "";
        }
        String ip = request.getHeader("x-forwarded-for");
        if (ip != null && ip.length() != 0 && !UNKNOWN.equalsIgnoreCase(ip)) {
            if (ip.contains(",")) {
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    public static String getClientIpAddr(HttpServletRequest request) {
        if (request == null) {
            return "";
        }
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_CLUSTER_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_FORWARDED");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_VIA");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("REMOTE_ADDR");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    /**
     * Sets request.
     *
     * @param request the request
     * @return the request
     */
    public ServletWebRequestHolder setRequest(HttpServletRequest request) {
        if (null == request) {
            throw new IllegalArgumentException("HttpServletRequest can not be null ");
        }
        this.request = request;
        return this;
    }

}
